import sys
from typing import Union
from xmlrpc.server import SimpleXMLRPCServer as SERVER
from xmlrpc.server import SimpleXMLRPCRequestHandler as HANDLER
import xmlrpc.client as CLIENT
import _thread as Thread
from CSYNAC.Node import Node
from CSYNAC.FunBase import FunsBase
import uuid
from functools import wraps

from CSYNAC.FunCentral import CentralFuns
from CSYNAC.Memery import Memery
from CSYNAC.FunNode import NodeFuns
import os

def SynacTask(func):
    task_id = uuid.uuid1()

    @wraps(func)
    def wrapper(*args: list, **kwargs):
        f = lambda: func(*args, **kwargs)
        Thread.start_new_thread(f, ())
        return task_id

    return wrapper


class RequestHandler(HANDLER):
    rpc_paths = ("/RPC2")


class BaseServer():
    def __init__(self, hostname:str="localhost",
                 nodename:str="BaseServer", listen_ip:str="0.0.0.0",port:int=8000,
                 bind_ip='localhost' ,back='normal', Funs=FunsBase):
        self.hostname = hostname
        self.bind_ip = bind_ip
        self.port = port
        self.listen_ip = listen_ip
        self.nodename = nodename
        self.run_in_backround = back
        self.m = Memery(nodename)
        self.funs_init()
        self.s = SERVER((listen_ip, port), requestHandler=RequestHandler, allow_none=True)
        self.funs = self.F(self.s, self.m)
        self._registry()
        self.group_init()

    def __del__(self):
        pass

    def funs_init(self, Funs=FunsBase):
        self.F = Funs

    def group_init(self):
        self.m.node_add(Node(
            nodename=self.nodename,
            hostname=self.hostname,
            host_ip='localhost',
            port=self.port
        ))

    def start(self, back: str = "None"):
        if back != "None":
            self.run_in_backround = back
        if self.run_in_backround == 'normal':
            self._start()
        elif self.run_in_backround == 'threading':
            def s():
                self._start()
            Thread.start_new_thread(s, ())
        elif self.run_in_backround == 'processing':
            log_path = "/var/log/Csync.log"
            def s():
                self._start()
            pid = os.fork()
            if pid==0:
                print(f"Starting the main node server with pid:{os.getpid()} on port {port}")
                os.setsid()
                os.umask(0)
                sys.stdout.flush()
                sys.stderr.flush()
                file = open(log_path, 'a+')
                sys.stdout = file
                sys.stderr = file
                Thread.start_new_thread(s, ())
            else:
                exit()
            # raise Exception("未写进程启动")
        else:
            raise Exception("设置了一个错误的启动方式,请在以下几项中选一个\n[normal,threading,processing]")

    def _start(self):
        self.s.serve_forever()
        self.s.__exit__()
        self._close()
        self._verbose("server_end")

    def _close(self):
        pass

    def _verbose(self, command: str):  # 输出该输出的信息,统一到一个函数之中
        if command == "server_end":
            print("服务器正常终止...")
            # pass
        else:
            raise Exception("Get the wrong command in verbose:{}".format(command))

    def _registry(self):  # 将函数注册到server中
        funs = self._funs()
        for fun_name in funs:
            self.s.register_function(funs[fun_name], fun_name)

    def _funs(self):
        return {
            "shutdown": self.funs.shutdown,
            "echo": self.funs.echo,
            "folder_report": self.funs.folder_report,
            "file_in": self.funs.file_in,
            "file_out": self.funs.file_out,
            "delete": self.funs.delete,
            "deletes": self.funs.deletes,
            "delete_folder": self.funs.delete_folder,
            "file_tree_build": self.funs.file_tree_build,
            "file_tree_check": self.funs.file_tree_check,
            "folder_md5s_calculate": self.funs.folder_md5s_calculate,
            "folder_md5s_fetch": self.funs.folder_md5s_fetch,
            "md5": self.funs.md5,
            "node_reg": self.funs.node_reg,
            "node_report": self.funs.node_report,
            "node_delete": self.funs.node_delete,
            'node_self':self.funs.node_self,
        }


class CentralServer(BaseServer):
    def __init__(self, nodename='CentralNode',
                 hostname="Localhost", listen_ip="0.0.0.0", port: int = 8000,
                 back='normal',bind_ip='localhost', Funs=CentralFuns):
        super(CentralServer, self).__init__(
            hostname=hostname,
            nodename=nodename,listen_ip=listen_ip, port=port,
            back=back, bind_ip=bind_ip, Funs=Funs
        )

    def funs_init(self, Funs=CentralFuns):
        self.F = Funs
    #
    # def group_init(self):
    #     pass
    #
    # def _close(self):
    #     pass
    #     # 服务器退出的时候执行
    #     # CentralServer 执行时向已注册的节点发出信息.
    pass


class NodeServer(BaseServer):
    def __init__(self, hostname: str = 'Localhost',
                 nodename='NodeServer', listen_ip: str = "0.0.0.0", port:int = 8000,
                 back='normal', bind_ip='localhost', Funs=NodeFuns,
                 central_server_ip="localhost", central_server_port=8000, reg_at_start=False):
        self.central_server_ip = central_server_ip
        self.central_server_port = central_server_port
        self.central_node: Union[Node, None] = None
        self.reg_at_start = reg_at_start
        super(NodeServer, self).__init__(
            hostname=hostname,
            nodename=nodename, listen_ip=listen_ip, port=port,
            back=back, bind_ip=bind_ip, Funs=Funs
        )

    def funs_init(self, Funs=NodeFuns):
        self.F = NodeFuns

    def group_init(self, ip: Union[str, None] =None, port: Union[int,None]=None):
        super(NodeServer, self).group_init()
        if ip and port:
            self.central_server_ip = ip
            self.central_server_port = port
        if self.reg_at_start:
            with CLIENT.ServerProxy(f"http://{self.central_server_ip}:{self.central_server_port}") as client:
                client: CentralFuns
                client.node_reg(self.nodename, self.hostname, self.bind_ip,self.port)
                print("注册成功")
                central_node_report:dict = client.node_report()
                # central_node = Node(**server_node)
                for node_name in central_node_report:
                    tmp_node = Node(**central_node_report[node_name])
                    self.m.node_add(tmp_node)
        else:
            print("不执行注册")

    def _funs(self):
        self.funs: NodeFuns
        old = super(NodeServer, self)._funs()
        new = {
            'push_folder': self.funs.push_folder,
            'pull_folder': self.funs.pull_folder,
        }
        return dict(old, **new)


def close_remote(ip, port):
    with CLIENT.ServerProxy("http://{}:{}".format(ip, port)) as client:
        print(client.shutdown())
